<?php

namespace app\service\tool;

use app\models\cacheModel\SettingModel;
use yii\base\Model;
use yii\helpers\Html;
use yii\helpers\VarDumper;

/**
 * 文件工具类
 * Class GenerateService
 * @package app\models
 */
class FileService extends Model
{

    /**
     * 创建一个目录
     * @param $path string 全路径
     * @return bool|string true 文件创建成功或者已经存在并且可以写入
     *                     string 文件存在没有权限或者创建失败
     */
    public static function mkdir($path, $throwError = true)
    {

        ### 是文件夹 && 已存在有权限
        if (is_dir($path) && is_writable($path)) return true;
        ### 是文件夹 && 没有权限 && 输出错误信息
        if (is_dir($path) && !is_writable($path) && $throwError) throw new \Exception(
            '该目录已经存在且没有写入权限！' . $path
        );
        ### 是文件夹 && 没有权限 && 不输出错误信息
        if (is_dir($path) && is_writable($path)) return false;

        try {
            mkdir($path, 0777, true);
            return true;
        } catch (\Exception $e) {
            if ($throwError) throw new \Exception(
                $e->getMessage() . ' 错误路径： ' . $path
            );
            return false;
        }
    }

    /**
     * 获取：CDN文件服务器主域名
     * @return array|mixed|string|null
     */
    public static function getCdnDomain()
    {
        // 上传类型为[1-本地上传]:无需添加上传域名
        $uploadType = SettingModel::getSetting('upload_type');
        if ($uploadType == 1) return \Yii::$app->request->hostInfo;

        return SettingModel::getSetting('aliOssCdnUrl');
    }

    /**
     * 判断：是否需要删除远程图片域名
     * @param string $content 检测内容
     * @return bool
     */
    public static function isNeedRmDomain($content)
    {
        if (is_array($content)) $content = VarDumper::dumpAsString($content);
        // 否则非本地图
        return !empty($content) && !empty(self::getCdnDomain()) && !strstr($content, self::getCdnDomain());
    }

    /**
     * 判断：是否图片本地图
     * @param string $src
     * @return bool
     */
    public static function isLocalImage($src = '')
    {

        // 取出远程域名的[HOST], example: https://www.baidu.com/aaa/sss/ccc.jpg, 得到[www.baidu.com]
        $remoteHost = parse_url(self::getCdnDomain());
        $host       = [
            empty($remoteHost['host']) ? '' : $remoteHost['host'],
            $_SERVER['HTTP_HOST']
        ];

        $imgHost = empty(parse_url($src)['host']) ? '' : parse_url($src)['host'];
        // 是列表则是本地图片了
        if (in_array($imgHost, $host)) return true;
        // 否则非本地图
        return false;
    }

    /**
     * 判断：文件上传完成用户发送的文件路径是否存在
     * @param $file
     * @return bool
     */
    public static function isExistsUploadFile($file)
    {
        $path = \Yii::getAlias('@webroot') . $file;
        if (!file_exists($path)) {

            return false;
        }

        return true;
    }

    /**
     * 获取：HTML内容里图片链接|必须是<img>中的src
     * @param string $html
     * @return mixed
     */
    public static function getHtmlImgList($html = '')
    {

        $html = Html::decode($html);
        preg_match_all('/<\s*img\s+[^>]*?src\s*=\s*([\'|\"])(.*?)\1[^>]*?\/?\s*>/i', $html, $match);

        return $match[2];
    }

    /**
     * 执行：添加图片服务器域名
     * @param string|array $path 操作的路径
     * @param string $style 图片样式(第三方设置，详细逻辑看方法内)
     * @param null $key 指定[$path]数组中的图片字段为操作对象
     * @return array|mixed|string
     */
    public static function addImgDomain($path = '', $style = 'w60', $key = null)
    {

        // 判断为空 直接返回null
        if (empty($path)) return '';

        ### 某些配置
        # 样式列表
        $smallList = [
            'none' => '', // 无样式
            'w60'  => 'x-oss-process=image/resize,w_60',
            'w150' => 'x-oss-process=image/resize,w_150',
            'w200' => 'x-oss-process=image/resize,w_200',
            'w300' => 'x-oss-process=image/resize,w_300',
        ];
        # 连接中有此字符串不进行叠加样式
        $filterStr = 'x-oss-process=';

        ### 某些初始化
        # 默认样式
        // $firstKey = array_keys($smallList)[1];
        $secondKey = array_keys($smallList)[1];
        if (!in_array($style, array_keys($smallList))) $style = $secondKey;
        // 最终样式
        $style = $smallList[$style];

        # 获取下params配置文件的第三方存储 查看域名
        $upYunHost = self::getCdnDomain();

        # 路径非数组 直接数组化
        $retStr = false; // 是否返回字符
        if (!is_array($path)) {
            $path   = [$path];
            $retStr = true;
        }

        // 数组 每个循环添加
        $stage = '';
        foreach ($path as $k => $v) {

            // 值是数组添加再次执行
            if (is_array($v)) {
                $path[$k] = self::addImgDomain($v, $style , $key);
                continue;
            }
            // 空||非图片链接 直接下一条
            if (empty($v) || !RequestService::isUrlImg($v)) continue;

            ### 先生成参数 + 原连接格式化
            $execUrl = $key && !empty($v[$key]) ? $v[$key] : $v;
            // 如果这个链接有[host]就不添加
            $stage = parse_url($execUrl);
            // 如果没有连域名添加
            if (empty($stage['host'])) {
                $execUrl = $upYunHost . $execUrl;
            }
            // 参数列表
            $params = explode('&', empty($stage['query']) ? '' : $stage['query']);
            // 样式参数叠入 + 生成
            if (!strstr($execUrl, $filterStr)) $params[] = $style;
            $params = array_filter($params);
            $params = !empty($params) ? '?' . implode('&', array_filter($params)) : '';

            ### 无[$key]参数的逻辑
            // 不是合法的域名 有相关版本号 无需添加
            if (!$key && strstr($execUrl, $filterStr)) {
                $path[$k] = $execUrl;
                continue;
            }
            // 是合法的域名则 无相关版本号 叠加后缀
            if (!$key) {
                $path[$k] = $execUrl . $params;
                continue;
            }

            ### 有[$key]参数的逻辑
            // 不是合法的域名 有相关版本号 无需添加
            if ($key && strstr($execUrl, $filterStr)) {
                $path[$k] = $execUrl;
                continue;
            }
            // 是合法的域名则 无相关版本号 叠加后缀
            if ($key) {
                $path[$k] = $execUrl . $params;
                continue;
            }
        }

        return $retStr && is_array($path) ? $path[0] : $path;
    }

    /**
     * 执行：HTMl - 添加图片服务器 host主机
     * @param string $html 文本HTML
     * @param string $style 图片样式(第三方设置，详细逻辑看方法内)
     * @return mixed|string
     */
    public static function addHtmlImgDomain($html = '', $style = 'w60')
    {

        // 空的返回
        if (empty($html)) return $html;
        // 解密下
        $html = Html::decode($html);

        $pre = "/src\s*=\s*['|\"]?\s*s*([^>\"'\s]*)/i";
        // 先匹配出图片列表
        preg_match_all($pre, $html, $imgSrcList);

        // 去空下
        $imgSrcList = array_filter($imgSrcList);

        // 检测下是否为空
        if (empty($imgSrcList)) return $html;

        // 获取云host
        $base    = [];
        $replace = [];
        // 拿出来循环
        foreach ($imgSrcList[1] as $k => $v) {

            // 空的直接下一条
            if (empty($v)) continue;

            // 有版本号 添加
            $base[]    = $v;
            $replace[] = self::addImgDomain($v, $style);
        }

        // 集体替换下
        $html = str_replace(array_unique($base), array_unique($replace), $html);

        return $html;
    }

    /**
     * 执行：移除图片列表的[HOST]
     * @param array $imgList 图片列表
     * @param array $imgField 图片字段列表
     * @return array
     */
    public static function delImgDomain($imgList = [], $imgField = [])
    {

        // 空的无需执行
        if (empty($imgList)) return $imgList;
        // [图片列表]非数组，组成组成列表
        $retStr = false; // 是否返回数组
        if (!is_array($imgList)) {
            $retStr  = true;
            $imgList = [$imgList];
        }

        // [图片字段列表]非数组，组成组成列表
        if (!is_array($imgField)) {
            $imgField = [$imgField];
        }

        // 循环操作
        $imgHost = self::getCdnDomain(); // 合法图片域名
        $imgHost = str_replace(['http://', 'https://'], '', $imgHost);
        foreach ($imgList as $k => $v) {

            // **** 一维数组 ****
            if (is_array($v)) {
                $imgList[$k] = self::delImgDomain($v, $imgField);
                continue;
            }

            // 有[合法图片字段] && 当前字段不合法 - 下一条数据
            if (!empty($imgField) && !in_array($k, $imgField)) continue;

            $pathInfo = parse_url($v);
            if (empty($pathInfo['host'])) continue;
            // 其他的就是检查域名 && 域名合法
            if (strstr($imgHost, $pathInfo['host'])) {
                $imgList[$k] = $pathInfo['path'];
                continue;
            }
            // 最后的话也就没有其他可能性了直接返回原数据
            $imgList[$k] = $v;
        }

        reset($imgList);
        return $retStr && is_array($imgList) ? current($imgList) : $imgList;
    }

    /**
     * 执行：HTMl - 添加图片服务器 host主机
     * @param string $html
     * @return mixed
     */
    public static function delHtmlImgDomain($html = '')
    {

        // 空的返回
        if (empty($html)) return $html;
        // 解密下
        $html = Html::decode($html);
        $pre  = "/src\s*=\s*['|\"]?\s*s*([^>\"'\s]*)/i";
        // 先匹配出图片列表
        preg_match_all($pre, $html, $imgSrcList);

        // 去空下
        $imgSrcList = array_filter($imgSrcList);
        // 检测下是否为空
        if (empty($imgSrcList)) return $html;

        $imgSrcList[2] = [];
        // 获取云host
        $imgHost = parse_url(self::getCdnDomain());
        $imgHost = str_replace(['http://', 'https://'], '', $imgHost);

        // 拿出来循环
        foreach ($imgSrcList[1] as $k => $v) {

            // 空的直接下一条
            if (empty($v)) {
                unset($imgSrcList[1][$k]);
                continue;
            }
            // 有域名 && 非图片域名
            if (strstr($v, 'http:') && !strstr($v, $imgHost)) {
                unset($imgSrcList[1][$k]);
                continue;
            }
            // 有https字样下一条
            if (strstr($v, 'https:') && !strstr($v, $imgHost)) {
                unset($imgSrcList[1][$k]);
                continue;
            }

            // 如果这个链接有[host]就不添加
            $stage = parse_url($v);
            // 有域名不操作 - 只是赋值
            if (!empty($stage['host']) || !empty($stage['scheme'])) {

                $imgSrcList[2][$k] = $stage['path'];
                continue;
            }

            $imgSrcList[2][$k] = $v;
            continue;
        }

        // 集体替换下
        $html = str_replace(array_unique($imgSrcList[1]), array_unique($imgSrcList[2]), $html);

        return $html;
    }

    /**
     * 获取文件夹下文件列表
     * @param string $path 路径
     * @param array $openPath 打开的文件列表
     * @param string $rootPath 基础文件夹
     * @return array
     */
    public static function getFileList($path = '', $openPath = [], $rootPath = '')
    {

        // 默认项目目录，如：/var/www/html
        if (!$rootPath) $rootPath = dirname(\Yii::getAlias('@webroot'));

        // 列举文件列表
        $file      = glob($path . '/*');
        $sortDesc  = count($file);
        $sort      = 0;
        $stageData = [];
        $firstName = array_shift($openPath); // 要打开文件夹的第一个
        foreach ($file as $k => $v) {

            $sort = $k;
            // 是文件
            if (is_file($v)) {
                $sort = $sortDesc;
                $sortDesc++;

                $stageData[$sort]['isFolder'] = -1;
            } else { // 是文件夹
                $stageData[$sort]['isFolder'] = 1;
            }

            $stageData[$sort]['name']        = pathinfo($v);
            $stageData[$sort]['folder_name'] = basename($stageData[$sort]['name']['dirname']);
            $stageData[$sort]['fileName']    = $stageData[$sort]['name']['filename'];
            $stageData[$sort]['name']        = $stageData[$sort]['name']['basename'];
            $stageData[$sort]['path']        = str_replace($rootPath, '', $v);
            $stageData[$sort]['id']          = md5($stageData[$sort]['path']);

            // 需要打开文件夹路径 在其中继续加载下面文件
            if ($stageData[$sort]['fileName'] == $firstName && $stageData[$sort]['isFolder'] == 1) {

                $stageData[$sort]['children'] = self::getFileList($v, $openPath);
            }
        }

        ksort($stageData);
        return array_values($stageData);
    }
}
